TCP/IP网络编程 —— 基于TCP的服务器端/客户端

前言

前面我们已经学习了创建套接字的函数socket()和向套接字分配地址的函数bind(),接下来我们将正式讨论如何通过套接字收发数据;同时我们之前也介绍了面向连接和面向消息的2种数据传输方式,这篇博客也将具体讨论面向连接的服务器端/客户端的编写。

理解TCP和UDP

根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字两种(本篇主要讲解TCP);因为TCP套接字是面向连接的,因此又称基于流(stream)的套接字。
这里推荐大家自行了解一下TCP/IP协议栈。

实现基于TCP的服务器端/客户端

TCP服务器端的默认函数调用顺序

绝大部分的TCP服务器端都按照以下的顺序调用函数:

1. socket()          ->创建套接字
2. bind()            ->分配套接字地址
3. listen()          ->等待连接请求状态
4. accept()          ->允许连接
5. read()/write()    ->数据交换
6. close()           ->断开连接

调用socket函数创建套接字,声明并初始化地址信息结构体变量,调用bind函数向套接字分配地址。这两个阶段之前已经讨论过了,下面讲解之后的几个过程。

进入等待连接请求状态

只有调用了listen函数,客户端才能进入可发出连接请求状态。也就是说,这时客户端才能调用connect函数(提前调用将发生错误)。

#include <sys/socket.h>
int listen(int sock, int backlog);
    ->成功时返回0,失败时返回-1。
    * sock        进入等待连接请求状态的套接字(服务器端套接字)文件描述符。
    * backlog     连接请求等待队列的长度,若为5,则表示最多使5个连接请求进入队列。

受理客户端连接请求

如果调用accept函数,函数将会自动创建一个新的套接字,用来连接到发起请求的客户端。

#include <sys/socket.h>
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
    ->成功时返回创建的套接字文件描述符,失败时返回-1。
    * sock        服务器套接字的文件描述符。
    * addr        客户端地址信息的变量地址值。
    * addrlen     第二个参数addr结构体的长度,即存有客户端地址长度的的变量地址。

TCP客户端的默认函数调用顺序

1. socket()          ->创建套接字
2. connect()         ->请求连接
3. read()/write()    ->交换数据
4. close()           ->断开连接

请求连接

#include <sys/socket.h>
int connect(int sock, struct sockaddr *servaddr, socklen_t *addrlen);
    ->成功时返回0,失败时返回-1。
    * sock        客户端套接字文件描述符
    * servaddr    保存服务器端地址信息的变量地址值
    * addrlen    第二个结构体参数servaddr的地址变量长度

客户端调用connect函数后,发生以下情况之一才会返回(完成函数调用)。

1. 服务器端接收连接请求。
2. 发生断网等异常情况而中断连接请求。

总结

TCP服务器端和客户端两者之间是交互的,并非互相独立,下面我们梳理一下整体流程:

服务器端创建套接字后连续调用bind,listen函数进入等待状态,客户端创建套接字后调用connect函数发起连接请求。(注意:客户端只有等到服务器端调用
listen函数后才能调用connect函数)同时要清楚,客户端调用connect函数之前,服务器端有可能率先调用了accept函数。此时,服务器端在调用accept函数
时进入阻塞状态,直到客户端调用connect函数为止。

自此,TCP服务器端及客户端的实现和相关函数的说明就已经介绍完了,具体示例可以回看前面几篇博客给出的demo,若还有不明白的地方,请多加复习。
本篇完结,白白~~